/*
 *  Copyright 2007-2008 by Matthias Buch-Kromann <mbk.isv@cbs.dk>.
 *  *
 *  This file is part of the Open-source Dependency Toolkit (OSDT),
 *  see http://code.google.com/p/open-source-dependency-toolkit.
 *  
 *  The OSDT is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as 
 *  published by the Free Software Foundation, either version 3 of 
 *  the License, or (at your option) any later version.
 * 
 *  The OSDT is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public 
 *  License along with the OSDT in the files COPYING and COPYING.LESSER.
 *  If not, see http://www.gnu.org/licenses.
 */

package org.osdtsystem.functions;

import org.osdtsystem.values.Augmentation;
import org.osdtsystem.values.Edge;
import org.osdtsystem.values.Layer;
import org.osdtsystem.values.Node;
import org.osdtsystem.functions.FunctionContext;
import org.osdtsystem.values.EdgeID;
import org.osdtsystem.values.Location;
import org.osdtsystem.values.NodeID;
import org.osdtsystem.values.PointedEdge;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class FunctionFactory {
    public static <V> Constant<V> 
            CONSTANT(V value) {
        return new Constant<V>(value);
    }
    
    // Global graph properties
    public static SingletonFunction<Layer> 
            LAYER(String name) {
        return new FuncLayer(name);
    }
    
    // Graph properties
    public static Function<PointedEdge>
            IN(SingletonFunction<Layer> layer, Function<? extends Node>... nodes) {
        return new FuncInEdges(layer, nodes);
    }

    public static Function<PointedEdge>
            IN(String layer, Function<? extends Node>... nodes) {
        return IN(LAYER(layer), nodes);
    }

    public static Function<PointedEdge>
            OUT(SingletonFunction<Layer> layer, Function<? extends Node>... nodes) {
        return new FuncOutEdges(layer, nodes);
    }

    public static Function<PointedEdge>
            OUT(String layer, Function<? extends Node>... nodes) {
        return OUT(LAYER(layer), nodes);
    }

    public static Function<NodeID> 
            INNODE(Function<? extends Edge>... edges) {
        return new FuncInNode(edges);
    }

    public static Function<NodeID>
            OUTNODE(Function<? extends Edge>... edges) {
        return new FuncOutNode(edges);
    }
    
    public static Function<NodeID>
            NODES(Function<? extends Node>... nodes) {
        return new FuncNodes(nodes);
    }

    public static Function<EdgeID>
            EDGES(Function<? extends Edge> edges) {
        return new FuncEdges(edges);
    }

    public static <V> SingletonFunction<V>
            FEATVAL(Class<V> runtimeclass, String feature,
                SingletonFunction<? extends Location> location) {
        return new FuncFeatureValue(runtimeclass, feature, location);
    }

    public static SingletonFunction<String>
            FEATVAL(String feature,
                SingletonFunction<? extends Location> location) {
        return FEATVAL(String.class, feature, location);
    }

    public static <V> Function<V>
            FEATVAL(Class<V> runtimeclass, String feature,
                Function<? extends Location> location) {
        FuncVariable<Location> var = new FuncVariable(Location.class);
        return MAP(var, location, new FuncFeatureValue(runtimeclass, feature, var));
    }

    public static Function<String>
            FEATVAL(String feature,
                Function<? extends Location> location) {
        FuncVariable<Location> var = new FuncVariable(Location.class);
        return MAP(var, location, new FuncFeatureValue(String.class, feature, var));
    }


    public static <V> Function<V>
            FUNCVAL(Class<V> runtimeclass, String function, 
                SingletonFunction<? extends Location> location) {
        return new FuncFunctionValue<V>(runtimeclass, function, location);
    }

    public static <V> Function<V>
            FUNCVAL(Class<V> runtimeclass, String function,
                Function<? extends Location> location) {
        FuncVariable<Location> var = new FuncVariable(Location.class);
        return MAP(var, location, THE(new FuncFunctionValue(runtimeclass, function, var)));
    }

    // Augmentations
    public static SingletonFunction<Augmentation> 
            AUGMENTATION(AugmentationFunction<?>... augmentations) {
        return new FuncAugmentation(augmentations);
    }

    public static AugmentationFunction<Augmentation> 
            SET(String node, String feat, Function<?> valueFunc) {
        return new FuncSet(node, feat, valueFunc);
    }
    
    
    public static <V> AugmentationFunction<V> THE(Function<? extends V> generator) {
        return new FuncThe(generator);
    }
    
    public static <V> AugmentationFunction<V> THE(FuncVariable<V> var, Function<? extends V> generator) {
        return new FuncThe(var, generator);
    }
    
    public static <V> AugmentationFunction<V> THE(String varname, Function<? extends V> generator) {
        return new FuncThe(varname, generator);
    }
        
    public static FuncVariable<NodeID> VARN() {
        return new FuncVariable(NodeID.class);
    }

    public static FuncVariable<NodeID> VARN(String name) {
        return new FuncVariable(NodeID.class, name);
    }
    
    public static FuncVariable<EdgeID> VARE() {
        return new FuncVariable(EdgeID.class);
    }
    
    public static FuncVariable<EdgeID> VARE(String name) {
        return new FuncVariable(EdgeID.class, name);
    }
    
    // Logical operations
    public static SingletonFunction<Boolean> 
            AND(SingletonFunction<Boolean>... arguments) {
        return new FuncAnd(arguments);
    }
    
    public static SingletonFunction<Boolean> 
            OR(SingletonFunction<Boolean>... arguments) {
        return new FuncOr(arguments);
    }

    public static SingletonFunction<Boolean> 
            NOT(SingletonFunction<Boolean> argument) {
        return new FuncNot(argument);
    }
    
    public static SingletonFunction<Boolean> 
            IMPLIES(SingletonFunction<Boolean> x, SingletonFunction<Boolean> y) {
        return new FuncImplies(x, y);
    }
    
    public static <V> SingletonFunction<Boolean>
            FORALL(FuncVariable<V> var, 
                Function<? extends V> generator, 
                SingletonFunction<Boolean> condition) {
        return new FuncForallExists(var, generator, condition, true);
    }
    
    public static <V> Function<Boolean>
            EXISTS(FuncVariable<V> var, 
                Function<? extends V> generator, 
                SingletonFunction<Boolean> condition) {
        return new FuncForallExists(var, generator, condition, false);
    }

    public static <V> SingletonFunction<Boolean>
            EQUALS(SingletonFunction<? extends V> arg1, SingletonFunction<? extends V> arg2) {
        return new FuncEquals(arg1, arg2);
    }
    
    // Numerical operations
    public static SingletonFunction<Double> 
            PRODUCT(Function<? extends Number>... numbers) {
        return new FuncProduct(numbers);
    }
    
    public static SingletonFunction<Double>
            SUM(Function<? extends Number>... numbers) {
        return new FuncSum(numbers);
    }
    
    public static SingletonFunction<Double> 
            MINUS(SingletonFunction<? extends Number> x, SingletonFunction<? extends Number> y) {
        return new FuncMinus(x, y);
    }
    
    public static SingletonFunction<Double>
            DIVIDE(SingletonFunction<? extends Number> x, SingletonFunction<? extends Number> y) {
        return new FuncDivide(x, y);
    }

    public static SingletonFunction<Double>
            NEG(SingletonFunction<? extends Number> x) {
        return MINUS(new Constant(new Double(0)), x);
    }


    public static SingletonFunction<Double>
            LOGSUMEXP(Function<? extends Number>... values) {
        return new FuncLogSumExp(values);
    }

    // public static <T extends Object & Comparable<? super T>>
    // T max(Collection<? extends T> coll)    
    public static <V extends Object & Comparable<? super V>> SingletonFunction<V>
            MAX(Function<? extends V>... arguments) {
        return new FuncMinMax(arguments, true);
    }

    public static <V,W extends Object & Comparable<? super W>> SingletonFunction<W>
            MAX(FuncVariable<V> var, Function<? extends V> generator, SingletonFunction<? extends W> value) {
        return MAX(MAP(var, generator, value));
    }

    public static <V extends Object & Comparable<? super V>> SingletonFunction<V>
            MIN(Function<? extends V>... arguments) {
        return new FuncMinMax(arguments, false);
    }

    public static <V,W extends Object & Comparable<? super W>> SingletonFunction<W>
            MIN(FuncVariable<V> var, Function<? extends V> generator, SingletonFunction<? extends W> value) {
        return MIN(MAP(var, generator, value));
    }

    public static <V,W extends Object & Comparable<? super W>> SingletonFunction<V>
            ARGMAX(FuncVariable<V> var, Function<? extends V> args, 
                SingletonFunction<? extends W> value) {
        return new FuncArgMinMax(var, args, value, true);
    }

    public static <V,W extends Object & Comparable<? super W>> SingletonFunction<V>
            ARGMIN(FuncVariable<V> var, Function<? extends V> args, 
                SingletonFunction<? extends W> value) {
        return new FuncArgMinMax(var, args, value, false);
    }
    
    // List operations
    public static SingletonFunction<String> 
            JOIN(String separator, Function<String>... values) {
        return new FuncJoin(separator, values, false);
    }

    // public interface Page<K extends Comparable<? super K>>
    //public class LocalPage<K extends Comparable<? super K>> implements Page<K>
        
    public static SingletonFunction<String>
            JOINSORTED(String separator, Function<String>... values) {
        return new FuncJoin(separator, values, true);
    }

    public static <V> Function<V>
            UNION(Function<? extends V>... sets) {
        return new FuncUnion<V>(sets);
    }
    
    public static <V> Function<V>
            INTSCT(Function<V>... sets) {
        return new FuncIntersection<V>(sets);
    }
    
    public static <V> Function<V>
            DIFF(Function<? extends V> x, Function<? extends V> y) {
        return new FuncDifference<V>(x, y);
    }

    public static <V> Function<V>
            SETOF(FuncVariable<V> var, Function<? extends V> generator,
                SingletonFunction<Boolean> condition) {
        return new FuncSetOf(var, generator, condition);
    }

    public static <V,W> Function<W>
            MAP(FuncVariable<V> var, Function<? extends V> generator,
                SingletonFunction<? extends W> mapper) {
        return new FuncMap(var, generator, mapper);
    }

    public static Function<Integer>
            SIZE(Function<?> subfunction) {
        return new FuncSize(subfunction);
    }

    public static SingletonFunction<Integer>
            STRLEN(SingletonFunction<? extends String> subfunction) {
        return new FuncStrlen(subfunction);
    }

    public static Function<Integer>
            STRLEN(Function<? extends String> subfunction) {
        FuncVariable<String> var = new FuncVariable(String.class);
        return MAP(var, subfunction, STRLEN(var));
    }

    public static FuncVariable<NodeID> THISNODE
            = new FuncVariable(NodeID.class, FunctionContext.ThisNodeVariableName);
    public static FuncVariable<EdgeID> THISEDGE
            = new FuncVariable(EdgeID.class, FunctionContext.ThisEdgeVariableName);
    public static AugmentationFunction<Augmentation> GIVEN 
            = new FuncGivenNew(true);
    public static AugmentationFunction<Augmentation> NEW 
            = new FuncGivenNew(false);    
}
